package com.sjf.JavaNIO.chatroom.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

/**
 * 聊天室服务器端
 *
 * @author <huangtang>
 * @since 2022/4/16 16:03
 **/
public class ChatServer {

    //服务器端的启动方法
    public void startServer () throws Exception {
        //1 创建Selector选择器
        Selector selector = Selector.open ();
        //2 创建ServerSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open ();
        //3 为channel通道绑定监听端口
        serverSocketChannel.bind (new InetSocketAddress (8888));
        //设置非阻塞模式
        serverSocketChannel.configureBlocking (false);
        //将通道注册到选择器中
        serverSocketChannel.register (selector , SelectionKey.OP_ACCEPT);
        System.out.println ("server is started");
        //4 循环等待有新的连接接入
        while (true) {
            //获取channel数量
            if ( selector.select () == 0 ) continue;

            //获取可用的Key
            Set<SelectionKey> selectionKeys = selector.selectedKeys ();
            Iterator<SelectionKey> iterator = selectionKeys.iterator ();
            //遍历集合
            while (iterator.hasNext ()) {
                SelectionKey selectionKey = iterator.next ();

                //移除set集合中当前selectionKey
                iterator.remove ();
                //5 根据就绪的状态进行相应的操作
                if ( selectionKey.isAcceptable () ) {
                    //accept状态
                    acceptOperator (selector , serverSocketChannel);
                }
                if ( selectionKey.isReadable () ) {
                    //read状态
                    readOperator (selector , selectionKey , 0);
                }
            }
        }
    }

    //read处理可读状态操作
    public static void readOperator ( Selector selector , SelectionKey selectionKey , Integer isClient ) throws IOException {
        // 从SelectionKey获取已经就绪的通道
        SocketChannel channel = (SocketChannel) selectionKey.channel ();
        // 创建buffer
        ByteBuffer byteBuffer = ByteBuffer.allocate (1024);
        // 循环去读取客户端发送的信息
        int readLength = channel.read (byteBuffer);
        StringBuilder message = new StringBuilder ();
        while (readLength > 0) {
            //重置缓冲区指针指向
            byteBuffer.flip ();
            //读取内容
            message.append (StandardCharsets.UTF_8.decode (byteBuffer));
            //重新读取缓冲区，查看是否已经到达末尾
            readLength = channel.read (byteBuffer);
        }
        // 将channel注册到选择器上，监听可读的状态
        channel.register (selector , SelectionKey.OP_READ);
        // 把客户端发送的信息广播到其他客户端
        if ( message.length () > 0 ) {
            System.out.println (message);
            //广播给其他客户端
            if ( isClient == 0 ) {
                castOtherClient (message , selector , channel);
            }
        }
    }

    //广播给其他客户端
    private static void castOtherClient ( StringBuilder message , Selector selector , SocketChannel channel ) throws IOException {
        // 获取所有已经接入的客户端
        Set<SelectionKey> selectionKeys = selector.keys ();
        // 循环向所有channel广播消息
        for ( SelectionKey selectionKey : selectionKeys ) {
            //获取需要广播到的通道
            Channel targetChannel = selectionKey.channel ();
            //不需要给自己发送
            if ( targetChannel instanceof SocketChannel && targetChannel != channel ) {
                //将消息写入该通道
                ((SocketChannel) targetChannel).write (StandardCharsets.UTF_8.encode (message.toString ()));
            }
        }
    }

    //accept处理接入状态操作
    private void acceptOperator ( Selector selector , ServerSocketChannel serverSocketChannel ) throws IOException {
        //获取接入的连接，即SocketChannel
        SocketChannel socketChannel = serverSocketChannel.accept ();
        //设置非阻塞
        socketChannel.configureBlocking (false);
        //把SocketChannel注册到选择器上,并且监听可读状态
        socketChannel.register (selector , SelectionKey.OP_READ);
        // 客户端回复信息
        socketChannel.write (StandardCharsets.UTF_8.encode ("已进入聊天室"));
    }

    //程序的入口
    public static void main ( String[] args ) {
        try {
            new ChatServer ().startServer ();
        } catch (Exception e) {
            e.printStackTrace ();
        }
    }
}
